package com.xzz.mybatis.generator.quartz;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xzz.mybatis.generator.common.MyResponseStatus;
import com.xzz.mybatis.generator.utils.MyCollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: hhz
 * @createDate: 2021-3-29
 */
@Controller
@RequestMapping("/quartz")
public class MyBaseQuartzController {
    @Resource
    private MyQuartzBeanPostProcessor quartzBeanPostProcessor;

    @Resource
    private QrtzRunningLogService qrtzRunningLogService;

    @Resource
    private Scheduler scheduler;

    @Resource
    private JdbcTemplate jdbcTemplate;

    private static final String TRIGGER_SUFFIX = "-trigger";

    /**
     * 管理 quartz 列表页面
     */
    @RequestMapping("/indexPage")
    public ModelAndView indexPage() {
        ModelAndView modelAndView = new ModelAndView("quartz_manage");
        modelAndView.addObject("xAuthToken", "");
        return modelAndView;
    }

    /**
     * quartz管理界面列表数据
     *
     * @param enable    true 启用 false 暂停
     * @param jobName   定时任务job名称，支持模糊搜索
     * @param jobDesc   任务描述，支持模糊搜索
     * @param isSuccess 是否运行成功
     */
    @GetMapping("/listQuartz")
    @ResponseBody
    public Map<String, Object> listQuartzDTO(@RequestParam(name = "enable", required = false) Boolean enable,
                                             @RequestParam(name = "isSuccess", required = false) Boolean isSuccess,
                                             @RequestParam(name = "jobDesc", required = false) String jobDesc,
                                             @RequestParam(name = "jobName", required = false) String jobName,
                                             @RequestParam("page") int page,
                                             @RequestParam("limit") int limit) throws SchedulerException {
        List<QuartzDTO> quartzDtos = new ArrayList<>(quartzBeanPostProcessor.getTaskMap().size());
        for (Map.Entry<JobDetail, Trigger> quartzEntry : quartzBeanPostProcessor.getTaskMap().entrySet()) {
            QuartzDTO newQuartzDTO = new QuartzDTO();
            if (CronTrigger.class.isAssignableFrom(quartzEntry.getValue().getClass())) {
                newQuartzDTO.setCron(
                        ((CronTrigger) scheduler.getTrigger(quartzEntry.getValue().getKey())).getCronExpression());
                Trigger.TriggerState triggerState = scheduler.
                        getTriggerState(quartzEntry.getValue().getKey());
                newQuartzDTO.setEnable(!Trigger.TriggerState.PAUSED.equals(triggerState));
            }
            newQuartzDTO.setJobName(quartzEntry.getKey().getKey().getName())
                    .setJobDesc(quartzEntry.getKey().getDescription());
            IPage<QrtzRunningLogDTO> logDtoPage = qrtzRunningLogService.getLatestByJobKey(
                    newQuartzDTO.getJobName(), 1, 1);
            if (MyCollectionUtil.isNotEmptyOrNull(logDtoPage.getRecords())) {
                QrtzRunningLogDTO latestDto = logDtoPage.getRecords().get(0);
                newQuartzDTO.setCreated(latestDto.getCreated());
                newQuartzDTO.setSpendTime(latestDto.getSpendTime());
                newQuartzDTO.setRunningState(latestDto.getRunningState());
            }
            quartzDtos.add(newQuartzDTO);
        }
        //按条件过滤
        quartzDtos = filterQuartzDTOS(enable, isSuccess, jobDesc, jobName, quartzDtos);
        int count = 0;
        //默认排序，启用，名称正序
        if (MyCollectionUtil.isNotEmptyOrNull(quartzDtos)) {
            count = quartzDtos.size();
            Collections.sort(quartzDtos, (o1, o2) -> {
                if (o1.getEnable() && !o2.getEnable()) return -1;
                else if (!o1.getEnable() && o2.getEnable()) return 1;
                else return o1.getJobName().compareTo(o2.getJobName());
            });
            quartzDtos = pagingQuartzDTOS(page, limit, quartzDtos);
        }
        Map<String, Object> reponse = new HashMap<>();
        reponse.put("code", "0");
        reponse.put("count", count);
        reponse.put("data", quartzDtos);
        return reponse;
    }

    /**
     * 获取本地 quartz
     * 暂时没有什么好的办法解决手抖重复触发问题, 因为拿不到准确任务跑的状态
     */
    @GetMapping("/start")
    @ResponseBody
    public MyResponseStatus triggerJob(@RequestParam("key") String jobKey) throws SchedulerException {
        Trigger.TriggerState triggerState = scheduler.getTriggerState(new TriggerKey(jobKey + TRIGGER_SUFFIX));
        if (Trigger.TriggerState.PAUSED.equals(triggerState)) {
            return MyResponseStatus.newBuilder().code(MyResponseStatus.FAIL_CODE)
                    .msg("任务已被禁用").build();
        } else {
            scheduler.triggerJob(JobKey.jobKey(jobKey));
            return MyResponseStatus.newBuilder().quickBuildSuccess();
        }
    }

    private List<QuartzDTO> pagingQuartzDTOS(@RequestParam("page") int page, @RequestParam("limit") int limit, List<QuartzDTO> quartzDtos) {
        //处理分页
        //1. 计算总页数
        int pages = (int) Math.ceil(quartzDtos.size() / 1.0 / limit);
        if (page >= pages) page = pages;
        int endOffset = limit * page > quartzDtos.size() ? quartzDtos.size() : limit * page;
        quartzDtos = quartzDtos.subList(limit * (page - 1), endOffset);
        return quartzDtos;
    }

    private List<QuartzDTO> filterQuartzDTOS(@RequestParam(name = "enable", required = false) Boolean enable, @RequestParam(name = "isSuccess", required = false) Boolean isSuccess, @RequestParam(name = "jobDesc", required = false) String jobDesc, @RequestParam(name = "jobName", required = false) String jobName, List<QuartzDTO> quartzDtos) {
        if (enable != null) {
            quartzDtos = quartzDtos.stream().filter(dto -> enable.equals(dto.getEnable())).collect(Collectors.toList());
        }
        if (isSuccess != null) {
            quartzDtos = quartzDtos.stream().filter(dto -> isSuccess.equals(dto.getRunningState())).collect(Collectors.toList());
        }
        if (StringUtils.isNoneBlank(jobDesc)) {
            quartzDtos = quartzDtos.stream().filter(dto -> dto.getJobDesc().toLowerCase().contains(jobDesc.toLowerCase())).collect(Collectors.toList());
        }
        if (StringUtils.isNoneBlank(jobName)) {
            quartzDtos = quartzDtos.stream().filter(dto -> dto.getJobName().toLowerCase().contains(jobName.toLowerCase())).collect(Collectors.toList());
        }
        return quartzDtos;
    }

    /**
     * 获取本地 quartz
     */
    @GetMapping("/allkey")
    @ResponseBody
    public MyResponseStatus getAllLocalQuartzJobKeys() {
        Set<String> jobKeys = quartzBeanPostProcessor.
                getAllLocalJobKeys().stream().map(JobKey::getName).collect(Collectors.toSet());
        return MyResponseStatus.newBuilder().quickBuildSingleSuccess(jobKeys);
    }

    /**
     * 禁用
     */
    @GetMapping("/disableJob")
    @ResponseBody
    public MyResponseStatus disableJob(@RequestParam("key") String jobKey) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(jobKey));
        return MyResponseStatus.newBuilder().quickBuildSuccess();
    }

    /**
     * 启用
     */
    @GetMapping("/enableJob")
    @ResponseBody
    public MyResponseStatus enableJob(@RequestParam("key") String jobKey) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(jobKey));
        return MyResponseStatus.newBuilder().quickBuildSuccess();
    }

    @GetMapping("/reschedule")
    @ResponseBody
    public MyResponseStatus reschedule(@RequestParam("key") String jobKey,
                                       @RequestParam("cron") String cron) throws SchedulerException {
        Trigger newTrigger = TriggerBuilder.newTrigger().
                withSchedule(CronScheduleBuilder.cronSchedule(cron))
                .withIdentity(jobKey + TRIGGER_SUFFIX)
                .build();
        scheduler.rescheduleJob(TriggerKey.triggerKey(jobKey + TRIGGER_SUFFIX), newTrigger);
        //pg sql
//        jdbcTemplate.update(
//                "INSERT INTO qrtz_cron_custom (job_name, cron_expression) VALUES (?,?)" +
//                        "ON CONFLICT (job_name) DO UPDATE SET cron_expression = ?", jobKey, cron, cron);
        //mysql sql
        jdbcTemplate.update(
                "INSERT INTO qrtz_cron_custom (job_name, cron_expression) VALUES (?,?)" +
                        "ON DUPLICATE KEY UPDATE cron_expression = ?", jobKey, cron, cron);
        return MyResponseStatus.newBuilder().quickBuildSuccess();
    }

    /**
     * 某个任务 详细运行日志
     */
    @GetMapping("/logPage")
    public ModelAndView logPage(@RequestParam("key") String jobKey) {
        ModelAndView modelAndView = new ModelAndView("quartz_list_log");
        modelAndView.addObject("xAuthToken", "");
        modelAndView.addObject("jobKey", jobKey);
        return modelAndView;
    }

    @GetMapping("/listLogs")
    @ResponseBody
    public Map<String, Object> listLogs(@RequestParam("key") String jobKey,
                                        @RequestParam("page") Integer currentPage,
                                        @RequestParam("limit") Integer pageSize) {
        IPage<QrtzRunningLogDTO> logDtoPage =
                qrtzRunningLogService.getLatestByJobKey(jobKey, pageSize, currentPage);
        Map<String, Object> retMap = new HashMap<>();
        retMap.put("code", "0");
        retMap.put("count", logDtoPage.getTotal());
        retMap.put("data", logDtoPage.getRecords());
        return retMap;
    }
}
